package maple.thelittleredbook.logging.aspect;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import maple.thelittleredbook.logging.annotation.SystemLog;
import maple.thelittleredbook.logging.entity.SysLog;
import maple.thelittleredbook.logging.service.SysLogService;
import maple.thelittleredbook.user.entity.User;
import maple.thelittleredbook.user.mapper.UserMapper;

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;


@Slf4j
@Aspect
@Component
public class SystemLogAspect {

    private final SysLogService sysLogService;
    private final UserMapper userMapper;
    private final ObjectMapper objectMapper;

    @Autowired
    public SystemLogAspect(SysLogService sysLogService, UserMapper userMapper) {
        this.sysLogService = sysLogService;
        this.userMapper = userMapper;
        this.objectMapper = new ObjectMapper();

        // 配置时间序列化
        this.objectMapper.registerModule(new JavaTimeModule());
        this.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        this.objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

        // 添加自定义序列化器，跳过MultipartFile字段
        SimpleModule module = new SimpleModule();
        module.addSerializer(MultipartFile.class, new JsonSerializer<MultipartFile>() {
            @Override
            public void serialize(MultipartFile value, JsonGenerator gen, SerializerProvider serializers)
                    throws IOException {
                if (value != null && !value.isEmpty()) {
                    gen.writeString(String.format("文件: %s (%d bytes)",
                            value.getOriginalFilename(), value.getSize()));
                } else {
                    gen.writeNull();
                }
            }
        });
        this.objectMapper.registerModule(module);
    }

    /**
     * 定义切点：所有标注了@SystemLog的方法
     */
    @Pointcut("@annotation(maple.thelittleredbook.logging.annotation.SystemLog)")
    public void logPointcut() {}

    /**
     * 环绕通知：在方法执行前后记录日志
     */
    @Around("logPointcut() && @annotation(systemLog)")
    public Object around(ProceedingJoinPoint joinPoint, SystemLog systemLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        SysLog sysLog = sysLogService.createNewLog();

        try {
            // 1. 记录请求信息
            setRequestInfo(sysLog, joinPoint, systemLog);
            setUserInfo(sysLog);

            // 2. 执行目标方法
            Object result = joinPoint.proceed();

            // 3. 记录成功响应
            setResponseInfo(sysLog, result, systemLog, 200);

            return result;

        } catch (Exception e) {
            // 4. 记录异常信息 - 关键是这里！
            setResponseInfo(sysLog, null, systemLog, 500);
            sysLog.setResponseData("执行异常：" + e.getMessage());

            throw e; // 重新抛出异常
        } finally {
            // 5. 无论成功还是失败，都记录耗时和保存日志
            long costTime = System.currentTimeMillis() - startTime;
            sysLog.setCostTime(costTime);

            // 确保日志一定被保存
            try {
                sysLogService.saveLog(sysLog);
            } catch (Exception logException) {
                log.error("保存日志失败: {}", logException.getMessage());
            }
        }
    }

    /**
     * 设置请求信息
     */
    private void setRequestInfo(SysLog sysLog, ProceedingJoinPoint joinPoint, SystemLog systemLog) {
        try {
            // 获取HttpServletRequest
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();

                // 设置请求URL和方法
                sysLog.setRequestUrl(request.getRequestURL().toString());
                sysLog.setRequestMethod(request.getMethod());

                // 设置请求IP
                sysLog.setRequestIp(getClientIpAddress(request));

                // 设置请求参数
                if (systemLog.saveRequestParam()) {
                    sysLog.setRequestParams(getRequestParams(joinPoint, request));
                }
            }

            // 设置操作描述
            sysLog.setOperation(systemLog.operation());

            // 设置方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            sysLog.setMethod(className + "." + methodName + "()");

        } catch (Exception e) {
            log.warn("设置请求信息失败: {}", e.getMessage());
        }
    }

    /**
     * 设置用户信息
     */
    private void setUserInfo(SysLog sysLog) {
        try {
            // 从Spring Security上下文获取用户信息
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null && authentication.isAuthenticated() && authentication.getPrincipal() instanceof Long) {
                Long userId = (Long) authentication.getPrincipal();
                sysLog.setUserId(userId);

                // 查询用户名
                User user = userMapper.selectById(userId);
                if (user != null) {
                    sysLog.setUsername(user.getUsername());
                }
            }
        } catch (Exception e) {
            log.warn("获取用户信息失败: {}", e.getMessage());
        }
    }

    /**
     * 设置响应信息
     */
    private void setResponseInfo(SysLog sysLog, Object result, SystemLog systemLog, int statusCode) {
        try {
            sysLog.setResponseCode(statusCode);

            if (systemLog.saveResponseData() && result != null) {
                // 将响应结果转为JSON字符串
                String responseJson = objectMapper.writeValueAsString(result);
                sysLog.setResponseData(responseJson);
            }
        } catch (Exception e) {
            log.warn("设置响应信息失败: {}", e.getMessage());
        }
    }

    /**
     * 获取请求参数
     */
    private String getRequestParams(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
        try {
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                StringBuilder params = new StringBuilder();
                for (Object arg : args) {
                    if (arg != null && !isFilterType(arg)) {
                        if (params.length() > 0) {
                            params.append(", ");
                        }

                        try {
                            // 尝试序列化参数
                            String paramJson = objectMapper.writeValueAsString(arg);
                            paramJson = filterSensitiveData(paramJson);
                            params.append(paramJson);
                        } catch (Exception serializeException) {
                            // 序列化失败时，记录对象基本信息
                            String fallbackInfo = String.format("对象类型: %s (序列化失败: %s)",
                                    arg.getClass().getSimpleName(),
                                    serializeException.getMessage());
                            params.append(fallbackInfo);
                            log.warn("参数序列化失败: {}", serializeException.getMessage());
                        }
                    }
                }
                return params.toString();
            }

            // 备选：获取查询参数
            String queryString = request.getQueryString();
            return StringUtils.hasText(queryString) ? queryString : null;

        } catch (Exception e) {
            log.warn("获取请求参数失败: {}", e.getMessage());
            return "获取参数失败：" + e.getMessage();
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (StringUtils.hasText(xForwardedFor) && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }

        String xRealIp = request.getHeader("X-Real-IP");
        if (StringUtils.hasText(xRealIp) && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }

        String proxyClientIp = request.getHeader("Proxy-Client-IP");
        if (StringUtils.hasText(proxyClientIp) && !"unknown".equalsIgnoreCase(proxyClientIp)) {
            return proxyClientIp;
        }

        String wlProxyClientIp = request.getHeader("WL-Proxy-Client-IP");
        if (StringUtils.hasText(wlProxyClientIp) && !"unknown".equalsIgnoreCase(wlProxyClientIp)) {
            return wlProxyClientIp;
        }

        return request.getRemoteAddr();
    }

    /**
     * 判断是否为需要过滤的参数类型
     */
    private boolean isFilterType(Object arg) {
        return arg instanceof HttpServletRequest
                || arg instanceof jakarta.servlet.http.HttpServletResponse
                || arg instanceof org.springframework.web.multipart.MultipartFile;
    }

    /**
     * 过滤敏感数据
     */
    private String filterSensitiveData(String jsonData) {
        if (!StringUtils.hasText(jsonData)) {
            return jsonData;
        }

        // 简单的敏感字段过滤，可以根据需要扩展
        String[] sensitiveFields = {"password", "oldPassword", "newPassword", "token", "secret"};
        String result = jsonData;

        for (String field : sensitiveFields) {
            // 使用正则表达式替换敏感字段的值
            result = result.replaceAll("\"" + field + "\"\\s*:\\s*\"[^\"]*\"", "\"" + field + "\":\"***\"");
        }

        return result;
    }
}